# modified workflow from ofek/hatch, big kudos to them.
name: Standalone build
on:
  workflow_call:
  workflow_dispatch:
  push:
    branches: [main]
    paths-ignore:
      - '*.md'
      - 'docs/**'
      - 'bazel/**'
      - 'typings/**'
      - 'changelog.d/**'
      - 'assets/**'
      - 'openllm-node/**'
      - 'Formula/**'
  pull_request:
    branches: [main]
    paths-ignore:
      - '*.md'
      - 'docs/**'
      - 'bazel/**'
      - 'typings/**'
      - 'changelog.d/**'
      - 'assets/**'
      - 'openllm-node/**'
      - 'Formula/**'
defaults:
  run:
    shell: bash --noprofile --norc -exo pipefail {0}
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true
env:
  APP_NAME: openllm
  PYOXIDIZER_VERSION: '0.24.0'
  HATCH_VERBOSE: 2
jobs:
  get_commit_message:
    name: Get commit message
    runs-on: ubuntu-latest
    if: "github.repository == 'bentoml/OpenLLM'" # Don't run on fork repository
    outputs:
      message: ${{ steps.commit_message.outputs.message }}
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # ratchet:actions/checkout@v4.1.1
        # Gets the correct commit message for pull request
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - name: Get commit message
        id: commit_message
        run: |
          set -xe
          COMMIT_MSG=$(git log --no-merges -1 --oneline)
          echo "message=$COMMIT_MSG" >> $GITHUB_OUTPUT
          echo github.ref ${{ github.ref }}
  python-artifacts:
    name: Build wheel and source distribution
    runs-on: ubuntu-latest
    needs: get_commit_message
    if: >-
      contains(needs.get_commit_message.outputs.message, '[binary build]') || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, '03 - Standalone Build')) || (github.event_name == 'push' && (startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/main')))
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # ratchet:actions/checkout@v4.1.1
        with:
          fetch-depth: 0
      - uses: bentoml/setup-bentoml-action@862aa8fa0e0c3793fcca4bfe7a62717a497417e4 # ratchet:bentoml/setup-bentoml-action@v1
        with:
          bentoml-version: 'main'
      - name: Pull latest change
        if: ${{ github.event_name != 'pull_request' }}
        run: git pull --autostash --no-edit --gpg-sign --ff origin main
      - name: Install build frontend
        run: python -m pip install --upgrade build
      - name: Build
        run: |
          bash local.sh
          python -m build -sw openllm-python/
      - name: Upload artifacts
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # ratchet:actions/upload-artifact@v3
        with:
          name: binary-artefacts
          path: openllm-python/dist/*
          if-no-files-found: error
  binaries:
    name: ${{ matrix.job.target }} (${{ matrix.job.os }})
    needs:
      - python-artifacts
    defaults:
      run:
        working-directory: openllm-python
    runs-on: ${{ matrix.job.os }}
    strategy:
      fail-fast: false
      matrix:
        job:
          # Linux
          - target: aarch64-unknown-linux-gnu
            os: ubuntu-22.04
            cross: true
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-22.04
            cross: true
          - target: x86_64-unknown-linux-musl
            os: ubuntu-22.04
            cross: true
          - target: i686-unknown-linux-gnu
            os: ubuntu-22.04
            cross: true
          - target: powerpc64le-unknown-linux-gnu
            os: ubuntu-22.04
            cross: true
          # Windows
          # - target: x86_64-pc-windows-msvc
          #   os: windows-2022
          # - target: i686-pc-windows-msvc
          #   os: windows-2022
          # macOS
          - target: aarch64-apple-darwin
            os: macos-12
          - target: x86_64-apple-darwin
            os: macos-12
    outputs:
      version: ${{ steps.version.outputs.version }}
    env:
      CARGO: cargo
      CARGO_BUILD_TARGET: ${{ matrix.job.target }}
      PYAPP_REPO: pyapp
      PYAPP_VERSION: '0.10.1'
      PYAPP_PIP_EXTERNAL: 'true'
    steps:
      - name: Checkout code
        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # ratchet:actions/checkout@v4.1.1
        with:
          fetch-depth: 0
      - name: Pull latest change
        if: ${{ github.event_name != 'pull_request' }}
        run: git pull --autostash --no-edit --gpg-sign --ff origin main
      - name: Fetch PyApp
        run: >-
          mkdir $PYAPP_REPO && curl -L https://github.com/ofek/pyapp/releases/download/v$PYAPP_VERSION/source.tar.gz | tar --strip-components=1 -xzf - -C $PYAPP_REPO
      - name: Set up Python
        uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # ratchet:actions/setup-python@v5.0.0
        with:
          python-version-file: .python-version-default
      - name: Install Hatch
        run: pip install -U hatch
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@78c6b5541adb5849f5d72d15da722aedb26327ca # ratchet:dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.job.target }}
      - name: Set up cross compiling
        if: matrix.job.cross
        uses: taiki-e/install-action@5794068c211bc8f5f4514b7c3f03d6adbe66f1b7 # ratchet:taiki-e/install-action@v2.26.18
        with:
          tool: cross
      - name: Configure cross compiling
        if: matrix.job.cross
        run: echo "CARGO=cross" >> $GITHUB_ENV
      - name: Configure target
        run: |-
          config_file="$PYAPP_REPO/.cargo/config_${{ matrix.job.target }}.toml"
          if [[ -f "$config_file" ]]; then
            mv "$config_file" "$PYAPP_REPO/.cargo/config.toml"
          fi
      - name: Download Python artifacts
        if: ${{ !startsWith(github.event.ref, 'refs/tags') }}
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # ratchet:actions/download-artifact@v3
        with:
          name: binary-artefacts
          path: openllm-python/dist
      - name: Configure embedded project
        if: ${{ !startsWith(github.event.ref, 'refs/tags') }}
        run: |-
          cd dist
          wheel="$(echo *.whl)"
          mv "$wheel" "../$PYAPP_REPO"
          echo "PYAPP_PROJECT_PATH=$wheel" >> $GITHUB_ENV
      - name: Build binary
        run: hatch build --target app
      # Windows installers don't accept non-integer versions so we ubiquitously
      # perform the following transformation: X.Y.Z.devN -> X.Y.Z.N
      - name: Set project version
        id: version
        run: |-
          old_version="$(hatch version)"
          version="${old_version/dev/}"

          if [[ "$version" != "$old_version" ]]; then
            cd dist/app
            find . -type f -iname "openllm-*" | while read -r file; do
              binary=$(echo "$file" | sed 's/dev//')
              mv "$file" "$binary"
            done
          fi

          echo "version=$version" >> $GITHUB_OUTPUT
          echo "$version"
      - name: Archive binary
        run: |-
          mkdir packaging
          cd dist/app
          find . -type f -iname "openllm-*" | while read -r file; do
          if [[ "$file" =~ -pc-windows- ]]; then
            7z a "../../packaging/${file:0:-4}.zip" "$file"
          else
            binary="${file/dev/}"
            chmod +x "$file"
            tar -czf "../../packaging/$binary.tar.gz" "$file"
          fi
          done
      - name: Upload staged archive
        if: runner.os != 'Linux'
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # ratchet:actions/upload-artifact@v3
        with:
          name: staged-${{ runner.os }}
          path: openllm-python/packaging/*
          if-no-files-found: error
      - name: Upload archive
        if: runner.os == 'Linux'
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # ratchet:actions/upload-artifact@v3
        with:
          name: standalone
          path: openllm-python/packaging/*
          if-no-files-found: error
  windows-packaging:
    name: Build Windows installers
    needs: binaries
    if: false # ${{ github.event_name != 'pull_request' }}
    runs-on: windows-2022
    env:
      VERSION: ${{ needs.binaries.outputs.version }}
    defaults:
      run:
        working-directory: openllm-python
    steps:
      - name: Checkout code
        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # ratchet:actions/checkout@v4.1.1
      - name: Set up Python
        uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # ratchet:actions/setup-python@v5.0.0
        with:
          python-version-file: .python-version-default
      - name: Pull latest change
        run: git pull --autostash --no-edit --gpg-sign --ff origin main
      - name: Install PyOxidizer ${{ env.PYOXIDIZER_VERSION }}
        run: pip install pyoxidizer==${{ env.PYOXIDIZER_VERSION }}
      - name: Download staged binaries
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # ratchet:actions/download-artifact@v3
        with:
          name: staged-${{ runner.os }}
          path: openllm-python/archives
      - name: Extract staged binaries
        run: |-
          mkdir -p bin
          for f in archives/*; do
            7z e "$f" -obin
          done
      # bin/<APP_NAME>-<VERSION>-<TARGET>.exe -> targets/<TARGET>/<APP_NAME>.exe
      - name: Prepare binaries
        run: |-
          mkdir -p targets
          for f in bin/*; do
            if [[ "$f" =~ openllm-${{ env.VERSION }}-(.+).exe$ ]]; then
              target="${BASH_REMATCH[1]}"
              mkdir -p "targets/$target"
              mv "$f" "targets/$target/${{ env.APP_NAME }}.exe"
            fi
          done
      - name: Build installers
        run: >-
          pyoxidizer build windows_installers --release --var version ${{ env.VERSION }}
      - name: Prepare installers
        run: |-
          mkdir installers
          mv build/*/release/*/*.{exe,msi} installers
      - name: Upload binaries
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # ratchet:actions/upload-artifact@v3
        with:
          name: standalone
          path: openllm-python/archives/*
          if-no-files-found: error
      - name: Upload installers
        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # ratchet:actions/upload-artifact@v3
        with:
          name: installers
          path: openllm-python/installers/*
